#include "downloadthread.h"
#include <QFile>
#include <QFileInfo>
#include <QDateTime>

#define SOH                     (0x01)  /* start of 128-byte data packet */
#define STX                     (0x02)  /* start of 1024-byte data packet */
#define EOT                     (0x04)  /* end of transmission */
#define ACK                     (0x06)  /* acknowledge */
#define NAK                     (0x15)  /* negative acknowledge */
#define CA                      (0x18)  /* two of these in succession aborts transfer */
#define CRC16                   (0x43)  /* 'C' == 0x43, request 16-bit CRC */

DownloadThread::DownloadThread(quint8 *StFlag, QMutex *Mutex)
{
    comx = nullptr;
    StopFlag = StFlag;
    mutex = Mutex;
}

void DownloadThread::OpenSeriaPort(QString PortName)
{
    if(comx != nullptr)
    {
        if(comx->isOpen())
        {
            comx->close();
        }
        delete comx;
        comx = nullptr;
        emit SeriaPortOff();
    }
    else
    {
        comx = new QSerialPort(PortName);
        comx->setBaudRate(QSerialPort::Baud115200);
        comx->setReadBufferSize(2048);
        comx->setDataBits(QSerialPort::Data8);
        comx->setFlowControl(QSerialPort::NoFlowControl);
        comx->setParity(QSerialPort::NoParity);
        comx->setStopBits(QSerialPort::OneStop);
        if(!comx->open(QIODevice::ReadWrite))
        {
            if(comx->isOpen())
            {
                comx->close();
            }
            delete comx;
            comx = nullptr;
            emit SeriaPortOff();
        }
        else
        {
            connect(comx,&QSerialPort::errorOccurred,[this](QSerialPort::SerialPortError error){
                if(error != QSerialPort::SerialPortError::NoError)
                {
                    if(comx->isOpen())
                    {
                        comx->close();
                    }
                    delete comx;
                    comx = nullptr;
                    emit SeriaPortOff();
                }
            });
            emit SeriaPortON();
        }
    }
}


quint8 DownloadThread::RecvPack(int timeout)
{
    if(comx->waitForReadyRead(timeout))
    {
        QByteArray buf = comx->readAll();
        return (quint8)buf.at(buf.size()-1);
    }
    else
    {
        return 0xff;
    }
}


quint16 UpdateCRC16(quint16 crcIn, quint8 byte)
{
     quint32 crc = crcIn;
     quint32 in = byte|0x100;
     do
     {
         crc <<= 1;
         in <<= 1;
         if(in&0x100)
         ++crc;
         if(crc&0x10000)
         crc ^= 0x1021;
     }while(!(in&0x10000));
     return crc&0xffffu;
}
quint16 Cal_CRC16(QByteArray buf)
{
     quint32 crc = 0;
     for(int i=0;i<buf.size();i++)
        crc = UpdateCRC16(crc,(quint8)buf.at(i));
     crc = UpdateCRC16(crc,0);
     crc = UpdateCRC16(crc,0);
     return crc&0xffffu;
}

void DownloadThread::DownLoad_Handler(QString FilePath)
{
    int reply = -1;//错误
    QByteArray FileName;
    quint32 FileSize = 0,SentSize = 0;
    quint8 PackNumber = 0,erros=0,BreakFlag = 0,SendStart = 0,SendEnd = 0,continueFlag = 0;
    QByteArray buf;
    char data[1024];
    int read_len = 0;
    QFile file(FilePath);
    if(!file.exists())
    {
        emit File_NotExist();
        emit DownLoad_IsSuccess(-3);
        return;
    }

    if(!file.open(QFile::ReadOnly))
    {
        emit File_OpenError();
        emit DownLoad_IsSuccess(-3);
        return;
    }

    QFileInfo FileInfo(file);

    FileName = FileInfo.fileName().toUtf8();
    FileSize = file.size();
    for(;;)
    {
        mutex->lock();
        if(*StopFlag == 1)
        {
            buf.clear();
            buf.append((quint8)CA);
            buf.append((quint8)CA);
            comx->write(buf);
            while(!comx->waitForBytesWritten(5000));
            *StopFlag = 0;
            mutex->unlock();
            emit DownLoad_IsSuccess(1);
            return;
        }
        mutex->unlock();

        switch (RecvPack(5000)) {
            case ACK:
            {
                if(SendEnd == 0)
                    PackNumber++;
                break;
            }
            case NAK:
            {
                break;
            }
            case CA:
            {
                BreakFlag = 1;
                break;
            }
            case CRC16:
            {
                SendStart++;
                if(SendStart>2)
                {
                    SendStart = 2;
                    erros++;
                    continueFlag = 1;
                }
                break;
            }
            default:
            {
                erros++;
                break;
            }
        }

        if(BreakFlag)
        {
            reply = -2;
            break;
        }
        else if(erros>=5)
        {
            buf.clear();
            buf.append((quint8)CA);
            buf.append((quint8)CA);
            comx->write(buf);
            while(!comx->waitForBytesWritten(5000));
            reply = -1;
            break;
        }
        else if(SendEnd >= 2)
        {
            reply = 0;
            break;
        }

        if(continueFlag == 1)
        {
            continueFlag = 0;
            continue;
        }

        buf.clear();

        if(SendStart == 1)//发送帧头
        {
            buf.append((quint8)SOH);
            buf.append((quint8)(0x00));
            buf.append((quint8)(0xFF));
            buf.append(FileName);
            buf.append((quint8)(0x00));

            int SizeLen,multiple10 = 0;

            for(int i=FileSize;i!=0;i/=10)
            {
                multiple10++;
            }

            for(int i=multiple10;i>0;i--)
            {
                SizeLen = FileSize;
                for(int j=i-1;j>0;j--)
                {
                   SizeLen /= 10;
                }
                SizeLen %= 10;
                buf.append((quint8)(SizeLen+0x30));
            }

            buf.append((quint8)(0x00));
            SizeLen = buf.size()-3;
            for(int i=128;i>SizeLen;i--)
            {
                buf.append((quint8)(0x00));
            }
            quint16 crc = Cal_CRC16(buf.mid(3,128));
            buf.append((quint8)(crc>>8));
            buf.append((quint8)(crc&0xFF));

            comx->write(buf);
            while(!comx->waitForBytesWritten(5000));
            PackNumber++;
        }
        else if(SendStart == 2)
        {
            memset(data,0,1024);
            read_len  = file.read(data,1024);
            if(read_len == 0)
            {
                if(SendEnd)
                {
                    buf.append((quint8)SOH);
                    buf.append((quint8)(0x00));
                    buf.append((quint8)(0xFF));
                    for(int i=0;i<128;i++)
                    {
                        buf.append((quint8)(0x00));
                    }
                    buf.append((quint8)(0x00));
                    buf.append((quint8)(0x00));
                }
                else
                {
                    buf.append((quint8)EOT);
                }

                comx->write(buf);
                while(!comx->waitForBytesWritten(5000));
                SendEnd++;
            }
            else if(read_len > 0)
            {
                buf.append((quint8)STX);
                buf.append((quint8)PackNumber);
                buf.append((quint8)(~(PackNumber)));
                buf.append(data,1024);
                quint16 crc = Cal_CRC16(buf.mid(3,1024));
                buf.append((quint8)(crc>>8));
                buf.append((quint8)(crc&0xFF));

                comx->write(buf);
                while(!comx->waitForBytesWritten(5000));
                SentSize += read_len;
                emit SendingProgress((int)(SentSize*100/FileSize));
            }
            else
            {
                buf.append((quint8)CA);
                buf.append((quint8)CA);
                comx->write(buf);
                while(!comx->waitForBytesWritten(5000));
                reply = -1;
                break;
            }
        }

    }

    emit DownLoad_IsSuccess(reply);
}
